package com.example.basic.utils.repeatsubmit;

import com.example.basic.web.service.redis.IRedisService;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Component;

import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.concurrent.TimeUnit;

import static com.example.basic.utils.DateUtils.getDatetimeFormat;
import static com.example.basic.web.enums.DataCodeEnum.USER_CODE;


@Component
public class RepeatSubmit {
    private static final Logger logger = LoggerFactory.getLogger(RepeatSubmit.class);

    @Autowired
    @Lazy
    private IRedisService iRedisService;

    /**
     * 防重复提交
     * @param pjp
     */
    public void repeatSubmit(ProceedingJoinPoint pjp, Object []  params) throws Exception {
        Signature signature = pjp.getSignature();
        MethodSignature methodSignature = (MethodSignature)signature;
        Method targetMethod = methodSignature.getMethod();
        if(targetMethod.isAnnotationPresent(Token.class)){
            Token token = targetMethod.getAnnotation(Token.class);
            List<Class> classN = Arrays.asList(token.cl());
            StringBuilder hashCode = new StringBuilder();
            String userId = getUserNo(pjp);
            String key = "RepeatSubmit_" + userId;
            hashCode.append(userId).append("-");
            try {
                getArrayObjectHashCode(hashCode,params,classN);
            } catch (IllegalAccessException e) {
                /**
                 * 代码bug
                 */
                logger.error(e.getMessage());
            }

            /**
             * redis操作
             */
            if(iRedisService.hget(key,hashCode.toString()) != null){
                //重复提交
                throw new Exception("不能重复提交!");
            }else {
                //有效期10秒钟
                iRedisService.hset(key,hashCode.toString(),"1");
                iRedisService.expire(key,1, TimeUnit.SECONDS);
            }
        }
    }

    /**
     * 获取用户id
     * @param pjp
     * @return
     */
    public static String getUserNo(ProceedingJoinPoint pjp){
        if (pjp.getArgs().length >= 1) {
            HttpServletRequest request = null;
            for (int i = 0; i < pjp.getArgs().length; i++) {
                if (pjp.getArgs()[i] instanceof HttpServletRequest) {
                    request = (HttpServletRequest) pjp.getArgs()[i];
                    break;
                }
            }
            if (request != null) {
                return (String) request.getAttribute("userCode");
            }
        }
        return "null";
    }

    /**
     * 遍历数组
     * @param hashCode
     * @param params
     * @param classN
     * @return
     * @throws IllegalAccessException
     */
    private static void getArrayObjectHashCode(StringBuilder hashCode, Object[] params, List<Class> classN) throws IllegalAccessException {
        for (Object o :params){
            //基本类型
            if(o == null || o instanceof Date || (o instanceof String && o != null && !"".equals(getDatetimeFormat(o.toString())))){
                continue;
            }
            //实体类
            if(classN != null){
                if( classN.contains(o.getClass())){
                    getClassObjectHashCode(hashCode,o,classN);
                    continue;
                }
            }
            //数组
            if(o.getClass().isArray() ){
                getArrayObjectHashCode(hashCode, (Object[]) o,classN);
                continue;
            }
            //列表
            if(o instanceof List){
                getArrayObjectHashCode(hashCode,((List) o).toArray(),classN);
                continue;
            }
            hashCode.append(o.hashCode());
        }
    }

    /**
     * 遍历实体类的属性
     * @param hashCode
     * @param classValue
     * @param classN
     * @return
     * @throws IllegalAccessException
     */
    private static void getClassObjectHashCode(StringBuilder hashCode, Object classValue, List<Class> classN) throws IllegalAccessException {
        for (Field field : classValue.getClass().getDeclaredFields()) {
            field.setAccessible(true);
            Object value = field.get(classValue);
            field.setAccessible(false);
            //基本类型
            if(value == null || value instanceof Date || (value instanceof String && value != null && !"".equals(getDatetimeFormat(value.toString())))){
                continue;
            }
            //实体类
            if(classN != null){
                if( classN.contains(value.getClass())){
                    getClassObjectHashCode(hashCode,value,classN);
                    continue;
                }
            }
            //数组
            if(value.getClass().isArray() ){
                getArrayObjectHashCode(hashCode, (Object[]) value,classN);
                continue;
            }
            //列表
            if(value instanceof List){
                getArrayObjectHashCode(hashCode,((List) value).toArray(),classN);
                continue;
            }
            hashCode.append(value.hashCode());
        }
    }
}
